package com.atguigu.tingshu.album.service.impl;

import com.atguigu.tingshu.album.mapper.AlbumAttributeValueMapper;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.AlbumStatMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.service.AlbumAttributeValueService;
import com.atguigu.tingshu.album.service.AlbumInfoService;
import com.atguigu.tingshu.common.cache.TingshuCache;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.model.album.AlbumAttributeValue;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.AlbumStat;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.query.album.AlbumInfoQuery;
import com.atguigu.tingshu.vo.album.AlbumAttributeValueVo;
import com.atguigu.tingshu.vo.album.AlbumInfoVo;
import com.atguigu.tingshu.vo.album.AlbumListVo;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Nullable;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class AlbumInfoServiceImpl extends ServiceImpl<AlbumInfoMapper, AlbumInfo> implements AlbumInfoService {

    @Autowired
    private AlbumInfoMapper albumInfoMapper;

    @Autowired
    private AlbumAttributeValueMapper albumAttributeValueMapper;

    @Autowired
    private AlbumStatMapper albumStatMapper;

    @Autowired
    private TrackInfoMapper trackInfoMapper;

    @Autowired
    private AlbumAttributeValueService albumAttributeValueService;

    @Autowired
    private RabbitService rabbitService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 保存专辑信息
     *
     * @param albumInfoVo 专辑信息视图对象，包含要保存的专辑基本信息及属性值列表
     * @param userId      用户ID，用于记录创建专辑的用户
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveAlbumInfo(AlbumInfoVo albumInfoVo, Long userId) {
        // 创建一个新的专辑信息对象，并从视图对象中复制属性
        AlbumInfo albumInfo = new AlbumInfo();
        BeanUtils.copyProperties(albumInfoVo, albumInfo);
        // 设置专辑的用户ID
        albumInfo.setUserId(userId);
        // 如果专辑不是免费类型，则设置默认的免费试听曲目数量
        if (!albumInfo.getPayType().equals(SystemConstant.ALBUM_PAY_TYPE_FREE)) {
            albumInfo.setTracksForFree(5);
        }
        // 设置专辑状态为已通过审核
        albumInfo.setStatus(SystemConstant.ALBUM_STATUS_PASS);
        // 插入专辑信息到数据库
        albumInfoMapper.insert(albumInfo);

        // 获取专辑属性值列表
        List<AlbumAttributeValueVo> albumAttributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList();
        // 如果专辑属性值列表不为空，则遍历并保存每个属性值
        if (!CollectionUtils.isEmpty(albumAttributeValueVoList)) {
            List<AlbumAttributeValue> attributeValueList = albumAttributeValueVoList.stream().map(albumAttributeValueVo -> {
                // 创建一个新的专辑属性值对象，并从视图对象中复制属性
                AlbumAttributeValue albumAttributeValue = new AlbumAttributeValue();
                BeanUtils.copyProperties(albumAttributeValueVo, albumAttributeValue);
                // 设置专辑ID
                albumAttributeValue.setAlbumId(albumInfo.getId());
                return albumAttributeValue;
            }).collect(Collectors.toList());
            // 批量插入专辑属性值到数据库
            this.albumAttributeValueService.saveBatch(attributeValueList);
        }
        // 保存专辑的播放统计信息
        this.saveAlbumStat(albumInfo.getId(), SystemConstant.ALBUM_STAT_PLAY);
        // 保存专辑的订阅统计信息
        this.saveAlbumStat(albumInfo.getId(), SystemConstant.ALBUM_STAT_SUBSCRIBE);
        // 保存专辑的浏览统计信息
        this.saveAlbumStat(albumInfo.getId(), SystemConstant.ALBUM_STAT_BROWSE);
        // 保存专辑的评论统计信息
        this.saveAlbumStat(albumInfo.getId(), SystemConstant.ALBUM_STAT_COMMENT);

        // 判断当前专辑字段isOpen 是否为上架
        // 判断当前专辑是否是上架下架
        if ("1".equals(albumInfo.getIsOpen())) {
            //  上架
            rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_UPPER, albumInfo.getId());
            // 添加布隆过滤器
            RBloomFilter<Object> bloomFilter = this.redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
            bloomFilter.add(albumInfo.getId());
        }
    }

    /**
     * 保存专辑统计数据
     * <p>
     * 该方法用于生成并保存专辑的统计信息，如播放次数等
     * 它创建一个专辑统计对象，设置专辑ID，统计类型，以及一个随机的统计数值
     *
     * @param albumId  专辑ID，用于标识特定的专辑
     * @param statPlay 统计类型，例如"播放次数"，用于定义统计的维度
     */
    private void saveAlbumStat(Long albumId, String statPlay) {
        // 创建一个新的专辑统计对象
        AlbumStat albumStat = new AlbumStat();
        // 设置专辑ID
        albumStat.setAlbumId(albumId);
        // 设置统计类型，例如"播放次数"
        albumStat.setStatType(statPlay);
        // 设置统计数值，这里使用随机数来模拟实际的统计值
        albumStat.setStatNum(new Random().nextInt(10000));
        // 将专辑统计对象插入到数据库中
        albumStatMapper.insert(albumStat);
    }

    /**
     * 查询用户专辑分页列表
     *
     * @param albumListVoPage
     * @param albumInfoQuery
     * @return
     */
    @Override
    public IPage<AlbumListVo> findUserAlbumPage(Page<AlbumListVo> albumListVoPage, AlbumInfoQuery albumInfoQuery) {
        // 调用mapper层方法
        return albumInfoMapper.selectUserAlbumPage(albumListVoPage, albumInfoQuery);
    }

    /**
     * 根据ID删除专辑信息
     * 此方法首先检查专辑下是否存在未删除的声音（音轨），如果存在，则抛出异常
     * 如果专辑下没有声音，则继续删除专辑信息及其相关属性
     *
     * @param id 专辑的唯一标识符
     * @throws GuiguException 如果专辑下存在未删除的声音时抛出此异常
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeAlbumInfoById(Long id) {
        // 创建查询条件，检查专辑下是否存在未删除的声音
        LambdaQueryWrapper<TrackInfo> groupQueryWrapper = new LambdaQueryWrapper<>();
        groupQueryWrapper.eq(TrackInfo::getAlbumId, id);
        // 执行查询，获取声音的数量
        Long count = trackInfoMapper.selectCount(groupQueryWrapper);
        // 如果专辑下存在未删除的声音，抛出异常，提示用户
        if (count > 0) {
            throw new GuiguException(400, "该专辑下存在未删除的声音！");
        }
        // 删除专辑信息
        this.removeById(id);
        // 删除与专辑相关的属性值信息
        albumAttributeValueMapper.delete(new LambdaQueryWrapper<AlbumAttributeValue>().eq(AlbumAttributeValue::getAlbumId, id));
        // 删除与专辑相关的统计信息
        albumStatMapper.delete(new LambdaQueryWrapper<AlbumStat>().eq(AlbumStat::getAlbumId, id));
    }

    /**
     * 根据专辑ID获取专辑信息
     * <p>
     * 此方法首先通过调用 {@link #getById(Long)} 方法从数据库中获取与给定ID关联的 {@link AlbumInfo} 对象
     * 如果找到对应的专辑信息，则进一步查询该专辑的所有属性值，并将其设置到专辑信息对象中
     *
     * @param id 专辑的唯一标识符
     * @return 返回 {@link AlbumInfo} 对象，包含专辑的详细信息和属性值列表如果找不到对应的专辑，则返回null
     */
    @TingshuCache(prefix = RedisConstant.ALBUM_INFO_PREFIX)
    @Override
    public AlbumInfo getAlbumInfoById(Long id) {
//        // 从缓存中获取专辑信息
//        String key = RedisConstant.ALBUM_INFO_PREFIX + id;
//        try {
//            // 根据key来获取缓存数据
//            AlbumInfo albumInfo = (AlbumInfo) this.redisTemplate.opsForValue().get(key);
//            // 判断
//            if (null == albumInfo) {
//                // 缓存中没有数据，则从数据库中查询数据，可能会造成缓存击穿，因此在此处加锁
//                String lockKey = RedisConstant.ALBUM_LOCK_SUFFIX + id;
//                // 获取锁对象
//                RLock lock = redissonClient.getLock(lockKey);
//                // 上锁
//                lock.lock();
//                try {
//                    // 为了充分利用缓存，查询数据库之前应该再查询一次缓存记录
//                    albumInfo = (AlbumInfo) this.redisTemplate.opsForValue().get(key);
//                    if (null != albumInfo) {
//                        System.out.println("缓存有数据");
//                        // 直接返回数据
//                        return albumInfo;
//                    }
//                    // 调用查询数据库方法
//                    albumInfo = getAlbumInfoDB(id);
//                    System.out.println("已查询数据库");
//                    // 从数据库中获取数据不一定有数据
//                    if (null == albumInfo) {
//                        // 缓存中没有数据，则设置一个临时数据，防止缓存穿透
//                        this.redisTemplate.opsForValue().set(key, new AlbumInfo(), RedisConstant.ALBUM_TEMPORARY_TIMEOUT, TimeUnit.SECONDS);
//                        // 返回一个空对象
//                        return new AlbumInfo();
//                    }
//                    // 数据库中有数据，直接放入缓存
//                    this.redisTemplate.opsForValue().set(key, albumInfo, RedisConstant.ALBUM_TEMPORARY_TIMEOUT, TimeUnit.DAYS);
//                    // 返回数据
//                    return albumInfo;
//                } catch (Exception e) {
//                    throw new RuntimeException(e);
//                } finally {
//                    // 解锁
//                    lock.unlock();
//                }
//            } else {
//                System.out.println("缓存有数据......");
//                // 返回缓存数据
//                return albumInfo;
//            }
//        } catch (RuntimeException e) {
//            log.error(e.getMessage());
//            // 调用发送短信与邮件的方法 通知管理员
//            // xxx方法
//        }
        // 兜底
        return getAlbumInfoDB(id);

    }

    @Nullable
    private AlbumInfo getAlbumInfoDB(Long id) {
        // 获取指定ID的专辑信息
        AlbumInfo albumInfo = this.getById(id);
        // 如果专辑信息存在，则查询并设置其属性值列表
        if (null != albumInfo) {
            // 查询与专辑ID关联的所有属性值
            List<AlbumAttributeValue> albumAttributeValueList = albumAttributeValueMapper.selectList(
                    new LambdaQueryWrapper<AlbumAttributeValue>().eq(AlbumAttributeValue::getAlbumId, id)
            );
            // 将查询到的属性值列表设置到专辑信息对象中
            albumInfo.setAlbumAttributeValueVoList(albumAttributeValueList);
        }
        // 返回完整的专辑信息对象
        return albumInfo;
    }

    /**
     * 根据专辑Id修改专辑信息
     *
     * @param id          专辑ID，用于标识要修改的专辑
     * @param albumInfoVo 包含专辑更新信息的视图对象，用于修改专辑信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateAlbumInfo(Long id, AlbumInfoVo albumInfoVo) {
        // 根据ID获取专辑信息
        AlbumInfo albumInfo = this.getById(id);
        // 将视图对象中的属性复制到专辑信息对象中，以进行修改
        BeanUtils.copyProperties(albumInfoVo, albumInfo);
        // 修改专辑信息
        this.updateById(albumInfo);

        // 删除专辑的现有属性值，以便修改
        albumAttributeValueMapper.delete(new LambdaQueryWrapper<AlbumAttributeValue>().eq(AlbumAttributeValue::getAlbumId, id));

        // 获取新的专辑属性值视图列表
        List<AlbumAttributeValueVo> albumAttributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList();
        // 如果新的专辑属性值列表不为空，则遍历并插入新的属性值
        if (!CollectionUtils.isEmpty(albumAttributeValueVoList)) {
            for (AlbumAttributeValueVo albumAttributeValueVo : albumAttributeValueVoList) {
                AlbumAttributeValue albumAttributeValue = new AlbumAttributeValue();
                // 将视图对象中的属性复制到专辑属性值对象中
                BeanUtils.copyProperties(albumAttributeValueVo, albumAttributeValue);
                // 设置专辑ID
                albumAttributeValue.setAlbumId(id);
                // 插入新的专辑属性值
                albumAttributeValueMapper.insert(albumAttributeValue);
            }

            //  判断当前专辑是否是上架下架
            if ("1".equals(albumInfo.getIsOpen())) {
                //  上架
                rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_UPPER, albumInfo.getId());
                // 添加布隆过滤器
                RBloomFilter<Object> bloomFilter = this.redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
                bloomFilter.add(albumInfo.getId());
            } else {
                //  下架
                rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_LOWER, albumInfo.getId());
            }
        }
    }

    /**
     * 根据用户ID查询用户所有的专辑列表
     *
     * @param userId 用户ID，用于查询特定用户的专辑信息
     * @return 返回用户专辑信息列表，最多包含30个专辑信息
     */
    @Override
    public List<AlbumInfo> findUserAllAlbumList(Long userId) {
        // 创建查询条件构造器
        LambdaQueryWrapper<AlbumInfo> queryWrapper = new LambdaQueryWrapper<>();
        // 设置查询条件，匹配用户ID
        queryWrapper.eq(AlbumInfo::getUserId, userId);
        // 按照相册ID降序排列，并限制查询结果数量为最多30条
        queryWrapper.orderByDesc(AlbumInfo::getId).last("limit 30");
        // 执行查询并返回结果
        return albumInfoMapper.selectList(queryWrapper);
    }

    /**
     * 根据专辑ID查找专辑属性值
     *
     * @param albumId 专辑ID，用于定位专辑属性值
     * @return 返回专辑属性值的列表
     */
    @Override
    public List<AlbumAttributeValue> findAlbumAttributeValueByAlbumId(Long albumId) {
        // 根据专辑ID查询专辑属性值
        return albumAttributeValueMapper.selectList(new LambdaQueryWrapper<AlbumAttributeValue>().eq(AlbumAttributeValue::getAlbumId, albumId));
    }

    /**
     * 根据专辑Id 获取到统计信息
     *
     * @param albumId
     * @return
     */
    @TingshuCache(prefix = "albumStat:info:")
    @Override
    public AlbumStatVo getAlbumStatVo(Long albumId) {
        return albumInfoMapper.selectAlbumStat(albumId);
    }

    /**
     * 更新专辑统计信息
     *
     * @param albumId
     * @param albumStatPlay
     * @param count
     */
    @Override
    public void updateAlbumStat(Long albumId, String statType, Integer count) {
        // 更新专辑统计信息
        albumInfoMapper.updateAlbumStat(albumId, statType, count);
    }

}
